<template>
  <el-container>
    <el-header
      style="
        display: flex;
        justify-content: center;
        align-items: center;
        height: 350px;
        padding: 0;
        background-image: url('https://img.neptech.net/i/2025/06/17/i84oaq.webp');
        background-position: center;
        background-size: cover;
      "
      ><h1 style="font-size: 40px; color: white">Woniu CTF 练习靶场</h1>
    </el-header>
    <el-container>
      <el-aside> </el-aside>
      <el-main>
        <div class="container">
          <!-- 骨架屏 -->
          <el-skeleton v-if="loading && exam.length === 0" :rows="6" animated />

          <!-- 靶场卡片列表 -->
          <div
            v-show="!loading || exam.length > 0"
            class="main-content"
            ref="contentContainer"
            v-infinite-scroll="loadMore"
            :infinite-scroll-disabled="noMoreData || loading"
            :infinite-scroll-distance="10"
          >
            <el-row :gutter="20" class="exam-row">
              <el-col
                v-for="(item, index) in exam"
                :key="item.rangeId"
                :span="6"
                style="margin-top: 20px"
              >
                <el-card
                  shadow="hover"
                  @click="openChallengeDialog(item)"
                  class="challenge-card"
                >
                  <template #header>
                    <div class="card-header">
                      <span class="text-contest-name">{{
                        item.rangeName
                      }}</span>
                      <el-tag
                        v-if="
                          containerStatusMap[item.rangeId]?.containerCreated
                        "
                        type="success"
                        size="small"
                        class="active-tag"
                      >
                        运行中
                      </el-tag>
                    </div>
                  </template>
                  <div class="card-content">
                    <div class="meta-info">
                      <div class="points">
                        <!--                  <span>{{ item.scoreValue }} pts</span>-->
                      </div>
                      <div class="challenge-difficulty">
                        <el-tag :type="getDifficultyType(item.difficultyId)">
                          {{ getDifficultyText(item.difficultyId) }}
                        </el-tag>
                      </div>
                      <div class="challenge-type">
                        <el-tag type="info">{{ item.rangeType }}</el-tag>
                      </div>
                    </div>

                    <div class="challenge-desc">
                      <el-tooltip :content="item.rangeDesc" placement="bottom">
                        <span class="desc-text">{{
                          truncateDescription(item.rangeDesc)
                        }}</span>
                      </el-tooltip>
                    </div>

                    <div
                      v-if="containerStatusMap[item.rangeId]?.containerCreated"
                      class="container-status"
                    >
                      <div class="status-item">
                        <span class="label">访问地址:</span>
                        <span class="value">{{
                          containerStatusMap[item.rangeId].challengeIpPort
                        }}</span>
                      </div>
                      <div class="status-item">
                        <span class="label">剩余时间:</span>
                        <span class="value time-remaining">
                          {{
                            formatTime(
                              containerStatusMap[item.rangeId].countdown
                            )
                          }}
                        </span>
                      </div>
                    </div>
                  </div>
                </el-card>
              </el-col>
            </el-row>

            <!-- 加载提示 -->
            <div v-if="loading && !noMoreData" class="loading-text">
              <i class="el-icon-loading"></i> 加载中...
            </div>
            <div v-if="noMoreData && exam.length > 0" class="no-more-data">
              没有更多数据了
            </div>
          </div>

          <!-- 弹窗：挑战详情 -->
          <el-dialog
            v-model="dialogVisible"
            :title="currentChallenge ? currentChallenge.rangeName : ''"
            width="50%"
            class="challenge-dialog"
            @closed="handleDialogClosed"
          >
            <div v-if="currentChallenge" class="challenge-details">
              <!-- 基本信息 -->
              <div class="challenge-info">
                <!--          <div class="info-item">
            <span class="label">靶场ID:</span>
            <span class="value">{{ currentChallenge.rangeId }}</span>
          </div>-->
                <div class="info-item">
                  <span class="label">类型:</span>
                  <span class="value">{{ currentChallenge.rangeType }}</span>
                </div>
                <div class="info-item">
                  <span class="label">难度:</span>
                  <el-tag
                    :type="getDifficultyType(currentChallenge.difficultyId)"
                  >
                    {{ getDifficultyText(currentChallenge.difficultyId) }}
                  </el-tag>
                </div>
                <!--          <div class="info-item">-->
                <!--            <span class="label">分值:</span>-->
                <!--            <span class="value">{{ currentChallenge.scoreValue }} pts</span>-->
                <!--          </div>-->
                <div class="info-item" v-if="currentChallenge.exposePort">
                  <span class="label">暴露端口:</span>
                  <span class="value">{{ currentChallenge.exposePort }}</span>
                </div>
              </div>

              <!-- 描述 -->
              <div class="challenge-description">
                <h3>靶场描述</h3>
                <p>{{ currentChallenge.rangeDesc }}</p>
              </div>

              <!-- 创建容器区域 -->
              <div
                class="container-actions"
                v-if="
                  !containerStatusMap[currentChallenge.rangeId]
                    ?.containerCreated
                "
              >
                <el-button
                  type="success"
                  @click="createContainer"
                  :loading="creatingContainer"
                >
                  创建容器
                </el-button>
              </div>

              <!-- 容器状态 -->
              <div
                v-if="
                  containerStatusMap[currentChallenge.rangeId]?.containerCreated
                "
                class="container-status-dialog"
              >
                <div class="ip-port-container">
                  <span class="label">访问地址:</span>
                  <span class="ip-port">{{
                    containerStatusMap[currentChallenge.rangeId].challengeIpPort
                  }}</span>
                  <el-tooltip content="复制地址" placement="top">
                    <el-button
                      icon="DocumentCopy"
                      circle
                      size="small"
                      @click="
                        copyAddress(
                          containerStatusMap[currentChallenge.rangeId]
                            .challengeIpPort
                        )
                      "
                    />
                  </el-tooltip>
                </div>

                <div class="time-container">
                  <el-tag type="info">
                    剩余时间:
                    {{
                      formatTime(
                        containerStatusMap[currentChallenge.rangeId].countdown
                      )
                    }}
                  </el-tag>
                  <el-button
                    type="danger"
                    @click="
                      destroyContainer(
                        containerStatusMap[currentChallenge.rangeId].containerId
                      )
                    "
                    size="small"
                  >
                    销毁容器
                  </el-button>
                </div>

                <div class="timeout-tip">
                  <el-alert
                    title="容器将在倒计时结束后自动销毁"
                    type="warning"
                    :closable="false"
                    show-icon
                  />
                </div>
              </div>

              <!-- Flag提交区域 -->
              <div v-if="currentChallenge" class="flag-submission">
                <h3>提交Flag</h3>
                <el-input
                  v-model="flagInput"
                  placeholder="请输入获取到的flag值"
                  class="flag-input"
                />
                <el-button
                  type="primary"
                  @click="submitFlag"
                  :loading="isSubmitting"
                  >提交</el-button
                >
              </div>
            </div>
          </el-dialog>

          <!-- 返回顶部按钮 -->
          <el-backtop
            target=".main-content"
            :visibility-height="300"
            :bottom="80"
          >
            <i class="el-icon-arrow-up"></i>
          </el-backtop>
        </div>
      </el-main>
      <el-aside> </el-aside>
    </el-container>
  </el-container>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from "vue";
import { ElMessage, ElTooltip, ElBacktop, ElAlert } from "element-plus";
import containerMange from "@/api/Backend/containerMange";
import rangeMange from "@/api/Backend/rangeMange";

// 定义挑战数据结构
interface Challenge {
  rangeId: string;
  rangeName: string;
  rangeUrl: string;
  rangeType: string;
  difficultyId: number;
  rangeDesc: string;
  exposePort: string;
  scoreValue: number;
}

// 容器状态接口
interface ContainerStatus {
  rangeId: string;
  containerId: string | null;
  challengeIpPort: string;
  countdown: number | null;
  countdownInterval: number | null;
  containerCreated: boolean;
  expiresAt?: number | null;
}

// 列表相关
const exam = ref<Challenge[]>([]);
const loading = ref(false);
const noMoreData = ref(false);
const creatingContainer = ref(false);

// 分页参数
const pageInfo = ref({
  pageNum: 1,
  pageSize: 8,
  total: 0,
});

// 弹窗与挑战详情
const dialogVisible = ref(false);
const currentChallenge = ref<Challenge | null>(null);

// 容器状态管理 - 每个靶场独立状态
const containerStatusMap = ref<Record<string, ContainerStatus>>({});

// 用户输入的flag
const flagInput = ref("");

// 初始化加载数据
onMounted(() => {
  loadExamList();
  restoreContainerStatuses();
});

// 组件卸载时清除所有定时器
onUnmounted(() => {
  Object.values(containerStatusMap.value).forEach((status) => {
    if (status.countdownInterval) {
      clearInterval(status.countdownInterval);
    }
  });
});

// 获取难度标签类型
const getDifficultyType = (difficulty: number) => {
  switch (difficulty) {
    case 1:
      return "success";
    case 2:
      return "warning";
    case 3:
      return "danger";
    default:
      return "info";
  }
};

// 获取难度文本
const getDifficultyText = (difficulty: number) => {
  switch (difficulty) {
    case 1:
      return "简单";
    case 2:
      return "中等";
    case 3:
      return "困难";
    default:
      return "未知";
  }
};

// 截断描述文本
const truncateDescription = (desc: string) => {
  if (!desc) return "暂无描述";
  return desc.length > 30 ? desc.substring(0, 30) + "..." : desc;
};

// 加载靶场列表
const loadExamList = async () => {
  if (noMoreData.value) return;

  loading.value = true;
  try {
    const res = await rangeMange.listPage(pageInfo.value);
    if (res.code === 200) {
      const newData = res.data.list.map((item: any) => ({
        ...item,
        scoreValue: item.difficultyId * 100,
      }));
      exam.value = [...exam.value, ...newData];
      pageInfo.value.total = res.data.total || 0;

      if (
        exam.value.length >= pageInfo.value.total ||
        newData.length < pageInfo.value.pageSize
      ) {
        noMoreData.value = true;
      }
    }
  } catch (err) {
    console.error("加载靶场列表失败:", err);
    ElMessage.error("加载数据失败，请稍后重试");
  } finally {
    loading.value = false;
  }
};

// 加载更多数据
const loadMore = async () => {
  if (loading.value || noMoreData.value) return;
  pageInfo.value.pageNum += 1;
  await loadExamList();
};

// 打开靶场详情对话框
const openChallengeDialog = (challenge: Challenge) => {
  currentChallenge.value = challenge;
  dialogVisible.value = true;

  // 初始化容器状态（如果不存在）
  if (!containerStatusMap.value[challenge.rangeId]) {
    containerStatusMap.value[challenge.rangeId] = {
      rangeId: challenge.rangeId,
      containerId: null,
      challengeIpPort: "",
      countdown: null,
      countdownInterval: null,
      containerCreated: false,
    };
  }
};

// 弹窗关闭时的处理
const handleDialogClosed = () => {
  currentChallenge.value = null;
  flagInput.value = "";
};

// 创建容器
const createContainer = async () => {
  if (!currentChallenge.value) return;

  const { rangeId, rangeName } = currentChallenge.value;
  const status = containerStatusMap.value[rangeId];

  if (status.containerCreated) {
    ElMessage.info("已有容器正在运行");
    return;
  }
  creatingContainer.value = true;
  try {
    const res = await containerMange.createStart({ rangeId, rangeName });
    if (res?.code === 200 && res.data) {
      // 更新容器状态
      containerStatusMap.value[rangeId] = {
        ...containerStatusMap.value[rangeId],
        containerId: res.data.containerId || null,
        challengeIpPort: res.data.socket || "",
        countdown: res.data.timeoutSeconds || 0,
        containerCreated: true,
        expiresAt: Date.now() + res.data.timeoutSeconds * 1000,
      };
      startCountdownForContainer(
        rangeId,
        containerStatusMap.value[rangeId].expiresAt!
      );
      saveContainerStatuses();
      ElMessage.success("容器创建成功");
    } else {
      ElMessage.error(res?.msg || "创建容器失败");
    }
  } catch (err: any) {
    console.error("创建容器失败:", err);
    ElMessage.error("创建容器失败: " + (err.message || "请稍后再试"));
  } finally {
    creatingContainer.value = false;
  }
};

// 为容器启动倒计时
const startCountdownForContainer = (rangeId: string, endTime: number) => {
  if (!containerStatusMap.value[rangeId]) {
    containerStatusMap.value[rangeId] = {
      rangeId,
      containerId: null,
      challengeIpPort: "",
      countdown: null,
      countdownInterval: null,
      containerCreated: false,
    };
  }

  const status = containerStatusMap.value[rangeId];

  if (status.countdownInterval) {
    clearInterval(status.countdownInterval);
    status.countdownInterval = null;
  }

  const updateCountdown = () => {
    const now = Date.now();
    const remaining = Math.max(0, Math.floor((endTime - now) / 1000));

    if (containerStatusMap.value[rangeId]) {
      containerStatusMap.value[rangeId].countdown = remaining;
    }

    if (remaining <= 0) {
      if (status.countdownInterval) {
        clearInterval(status.countdownInterval);
      }
      if (status.containerId) {
        destroyContainer(status.containerId);
      }
    }
  };

  updateCountdown();
  status.countdownInterval = window.setInterval(updateCountdown, 1000);
};

// 销毁容器
const destroyContainer = async (containerId: string) => {
  if (!containerId) {
    ElMessage.error("无效的容器ID");
    return;
  }
  try {
    await containerMange.destroy({ containerId });

    const rangeId = Object.keys(containerStatusMap.value).find(
      (key) => containerStatusMap.value[key].containerId === containerId
    );

    if (rangeId) {
      if (containerStatusMap.value[rangeId].countdownInterval) {
        clearInterval(containerStatusMap.value[rangeId].countdownInterval!);
      }

      containerStatusMap.value[rangeId] = {
        rangeId,
        containerId: null,
        challengeIpPort: "",
        countdown: null,
        countdownInterval: null,
        containerCreated: false,
      };

      saveContainerStatuses();
      ElMessage.success("容器已销毁");
    }
  } catch (err: any) {
    console.error("销毁容器失败:", err);
    ElMessage.error("销毁容器失败: " + (err.message || "请稍后再试"));
  }
};

// 格式化倒计时时间
const formatTime = (seconds: number | null): string => {
  if (seconds === null || seconds <= 0) return "00:00:00";
  const hours = Math.floor(seconds / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);
  const secs = seconds % 60;
  return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(
    2,
    "0"
  )}:${String(secs).padStart(2, "0")}`;
};

// 复制文本到剪贴板
const copyAddress = (text: string) => {
  navigator.clipboard
    .writeText(text)
    .then(() => {
      ElMessage.success("复制成功");
    })
    .catch(() => {
      ElMessage.error("复制失败，请手动选择复制");
    });
};

// 保存容器状态到本地存储
const saveContainerStatuses = () => {
  const activeContainers = Object.values(containerStatusMap.value)
    .filter((status) => status.containerCreated)
    .map((status) => ({
      rangeId: status.rangeId,
      containerId: status.containerId,
      challengeIpPort: status.challengeIpPort,
      countdown: status.countdown,
      containerCreated: status.containerCreated,
      expiresAt: status.expiresAt,
    }));

  localStorage.setItem("activeContainers", JSON.stringify(activeContainers));
};

// 从本地存储恢复容器状态
const restoreContainerStatuses = () => {
  const savedContainers = localStorage.getItem("activeContainers");
  if (!savedContainers) return;

  try {
    const containers = JSON.parse(savedContainers);
    const now = Date.now();

    containers.forEach((container: any) => {
      if (container.expiresAt && container.expiresAt > now) {
        const remainingSeconds = Math.floor((container.expiresAt - now) / 1000);

        containerStatusMap.value[container.rangeId] = {
          rangeId: container.rangeId,
          containerId: container.containerId,
          challengeIpPort: container.challengeIpPort,
          countdown: remainingSeconds,
          countdownInterval: null,
          containerCreated: true,
          expiresAt: container.expiresAt,
        };

        startCountdownForContainer(container.rangeId, container.expiresAt);
      }
    });
  } catch (err) {
    console.error("恢复容器状态失败:", err);
    localStorage.removeItem("activeContainers");
  }
};

// 提交flag,需要将容器id和flag值传入后端进行对比
const isSubmitting = ref(false);

const submitFlag = async () => {
  if (isSubmitting.value) return;

  if (!currentChallenge.value || !flagInput.value.trim()) {
    ElMessage.warning("请先选择靶场并输入flag");
    return;
  }

  const rangeId = currentChallenge.value.rangeId;
  const containerId = containerStatusMap.value[rangeId]?.containerId;

  if (!containerId) {
    ElMessage.warning("当前靶场未创建容器");
    return;
  }

  isSubmitting.value = true;

  try {
    const res = await containerMange.compareFlag({
      containerId,
      inputFlag: flagInput.value.trim(),
    });

    if (res.code === 200) {
      ElMessage.success("恭喜！Flag正确");
      // 停止倒计时
      if (containerStatusMap.value[rangeId]?.countdownInterval) {
        clearInterval(containerStatusMap.value[rangeId].countdownInterval!);
        containerStatusMap.value[rangeId].countdownInterval = null;
      }
      // 销毁容器（可选）
      await destroyContainer(containerId);
      // 重置容器状态
      containerStatusMap.value[rangeId] = {
        rangeId,
        containerId: null,
        challengeIpPort: "",
        countdown: null,
        countdownInterval: null,
        containerCreated: false,
      };
      saveContainerStatuses();
      // 关闭弹窗
      dialogVisible.value = false;
      currentChallenge.value = null;
    } else if (res.code === 501) {
      ElMessage.warning(res.msg || "时间到");
    } else {
      ElMessage.error(res.msg || "Flag不正确，请再试一次");
    }
  } catch (err: any) {
    ElMessage.error("验证失败：" + (err.message || "请稍后再试"));
    console.error(err);
  } finally {
    isSubmitting.value = false;
  }
};
</script>

<style scoped>
.container {
  padding: 20px;
  font-family: "Helvetica Neue", sans-serif;
}

.main-content {
  overflow: auto;
  max-height: calc(100vh - 60px);
  scrollbar-width: none;
}

.main-content::-webkit-scrollbar {
  display: none;
}

.exam-row {
  margin-bottom: 20px;
}

.loading-text,
.no-more-data {
  text-align: center;
  margin: 20px 0;
  color: #999;
}

.no-more-data {
  padding-bottom: 20px;
}

.challenge-card {
  margin-bottom: 20px;
  cursor: pointer;
  transition: transform 0.2s;
  height: 100%;
  display: flex;
  flex-direction: column;
}

.challenge-card:hover {
  transform: translateY(-5px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.card-header {
  font-size: 16px;
  font-weight: bold;
  padding: 12px;
  border-bottom: 1px solid #ebebeb;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.active-tag {
  margin-left: 8px;
}

.card-content {
  padding: 12px;
  flex: 1;
  display: flex;
  flex-direction: column;
}

.meta-info {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 12px;
}

.points {
  font-weight: bold;
  color: #409eff;
}

.challenge-difficulty,
.challenge-type {
  display: inline-block;
}

.challenge-desc {
  margin: 10px 0;
  flex: 1;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 1.5;
  color: #666;
  font-size: 13px;
}

.desc-text {
  color: #666;
  font-size: 13px;
}

.container-status {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px dashed #eee;
}

.status-item {
  display: flex;
  margin-bottom: 6px;
  font-size: 13px;
}

.status-item .label {
  color: #666;
  min-width: 60px;
}

.status-item .value {
  color: #333;
  word-break: break-all;
}

.time-remaining {
  color: #67c23a;
  font-family: monospace;
}

/* 弹窗样式 */
.challenge-details {
  padding: 0 10px;
}

.challenge-info {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  margin-bottom: 20px;
}

.info-item {
  display: flex;
  align-items: center;
}

.info-item .label {
  font-weight: bold;
  margin-right: 8px;
  color: #666;
  min-width: 70px;
}

.info-item .value {
  color: #333;
}

.challenge-description {
  margin: 20px 0;
  padding: 15px;
  border-radius: 4px;
}

.challenge-description h3 {
  margin-top: 0;
  margin-bottom: 10px;
}

.challenge-description p {
  margin: 0;
  line-height: 1.6;
}

.container-actions {
  margin: 20px 0;
  text-align: center;
}

.container-status-dialog {
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px solid #eee;
}

.ip-port-container {
  display: flex;
  align-items: center;
  margin-bottom: 15px;
}

.ip-port-container .label {
  font-weight: bold;
  margin-right: 8px;
  color: #666;
}

.ip-port {
  margin-right: 10px;
  color: #409eff;
  font-family: monospace;
  word-break: break-all;
}

.time-container {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 15px;
}

.timeout-tip {
  margin-top: 15px;
}

.flag-submission {
  margin-top: 20px;
}

.flag-input {
  width: 100%;
  margin-bottom: 10px;
}

.submit-btn {
  width: 100%;
}

@media (max-width: 1200px) {
  .el-col {
    width: 50%;
  }
}

@media (max-width: 768px) {
  .el-col {
    width: 100%;
  }

  .challenge-dialog {
    width: 90%;
  }

  .challenge-info {
    grid-template-columns: 1fr;
  }

  .ip-port-container {
    flex-wrap: wrap;
    gap: 8px;
  }
}
</style>
